; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc --mtriple=loongarch64 < %s | FileCheck %s

;; Test generation of the bstrins.d instruction.
;; There are 8 patterns that can be matched to bstrins.d. See performORCombine
;; for details.

;; Pattern 1
;; R = or (and X, mask0), (and (shl Y, lsb), mask1)
;; =>
;; R = BSTRINS X, Y, msb, lsb
define i64 @pat1(i64 %a, i64 %b) nounwind {
; CHECK-LABEL: pat1:
; CHECK:       # %bb.0:
; CHECK-NEXT:    bstrins.d $a0, $a1, 39, 16
; CHECK-NEXT:    ret
  %and1 = and i64 %a, -1099511562241  ; 0xffffff000000ffff
  %shl = shl i64 %b, 16
  %and2 = and i64 %shl, 1099511562240 ; 0x000000ffffff0000
  %or = or i64 %and1, %and2
  ret i64 %or
}

define i64 @pat1_swap(i64 %a, i64 %b) nounwind {
; CHECK-LABEL: pat1_swap:
; CHECK:       # %bb.0:
; CHECK-NEXT:    bstrins.d $a0, $a1, 39, 16
; CHECK-NEXT:    ret
  %and1 = and i64 %a, -1099511562241  ; 0xffffff000000ffff
  %shl = shl i64 %b, 16
  %and2 = and i64 %shl, 1099511562240 ; 0x000000ffffff0000
  %or = or i64 %and2, %and1
  ret i64 %or
}

;; Pattern 2
;; R = or (and X, mask0), (shl (and Y, mask1), lsb)
;; =>
;; R = BSTRINS X, Y, msb, lsb
define i64 @pat2(i64 %a, i64 %b) nounwind {
; CHECK-LABEL: pat2:
; CHECK:       # %bb.0:
; CHECK-NEXT:    bstrins.d $a0, $a1, 39, 16
; CHECK-NEXT:    ret
  %and1 = and i64 %a, -1099511562241 ; 0xffffff000000ffff
  %and2 = and i64 %b, 16777215       ; 0x0000000000ffffff
  %shl = shl i64 %and2, 16
  %or = or i64 %and1, %shl
  ret i64 %or
}

define i64 @pat2_swap(i64 %a, i64 %b) nounwind {
; CHECK-LABEL: pat2_swap:
; CHECK:       # %bb.0:
; CHECK-NEXT:    bstrins.d $a0, $a1, 39, 16
; CHECK-NEXT:    ret
  %and1 = and i64 %a, -1099511562241 ; 0xffffff000000ffff
  %and2 = and i64 %b, 16777215       ; 0x0000000000ffffff
  %shl = shl i64 %and2, 16
  %or = or i64 %shl, %and1
  ret i64 %or
}

;; Pattern 3
;; R = or (and X, mask0), (and Y, mask1)
;; =>
;; R = BSTRINS X, (srl (and Y, mask1), lsb), msb, lsb
define i64 @pat3(i64 %a, i64 %b) nounwind {
; CHECK-LABEL: pat3:
; CHECK:       # %bb.0:
; CHECK-NEXT:    andi $a1, $a1, 288
; CHECK-NEXT:    srli.d $a1, $a1, 4
; CHECK-NEXT:    bstrins.d $a0, $a1, 11, 4
; CHECK-NEXT:    ret
  %and1 = and i64 %a, -4081 ; 0xfffffffffffff00f
  %and2 = and i64 %b, 288   ; 0x0000000000000120
  %or = or i64 %and1, %and2
  ret i64 %or
}

define i64 @pat3_swap(i64 %a, i64 %b) nounwind {
; CHECK-LABEL: pat3_swap:
; CHECK:       # %bb.0:
; CHECK-NEXT:    andi $a1, $a1, 288
; CHECK-NEXT:    srli.d $a1, $a1, 4
; CHECK-NEXT:    bstrins.d $a0, $a1, 11, 4
; CHECK-NEXT:    ret
  %and1 = and i64 %a, -4081 ; 0xfffffffffffff00f
  %and2 = and i64 %b, 288   ; 0x0000000000000120
  %or = or i64 %and2, %and1
  ret i64 %or
}

;; Pattern 4
;; R = or (and X, mask), (shl Y, shamt)
;; =>
;; R = BSTRINS X, Y, 63, shamt
define i64 @pat4(i64 %a, i64 %b) nounwind {
; CHECK-LABEL: pat4:
; CHECK:       # %bb.0:
; CHECK-NEXT:    bstrins.d $a0, $a1, 63, 8
; CHECK-NEXT:    ret
  %and = and i64 %a, 255
  %shl = shl i64 %b, 8
  %or = or i64 %and, %shl
  ret i64 %or
}

define i64 @pat4_swap(i64 %a, i64 %b) nounwind {
; CHECK-LABEL: pat4_swap:
; CHECK:       # %bb.0:
; CHECK-NEXT:    bstrins.d $a0, $a1, 63, 8
; CHECK-NEXT:    ret
  %and = and i64 %a, 255
  %shl = shl i64 %b, 8
  %or = or i64 %shl, %and
  ret i64 %or
}

;; Pattern 5
;; R = or (and X, mask0), const
;; =>
;; R = BSTRINS X, (const >> lsb), msb, lsb
define i64 @pat5(i64 %a) nounwind {
; CHECK-LABEL: pat5:
; CHECK:       # %bb.0:
; CHECK-NEXT:    lu12i.w $a1, 74565
; CHECK-NEXT:    ori $a1, $a1, 1656
; CHECK-NEXT:    bstrins.d $a0, $a1, 47, 16
; CHECK-NEXT:    ret
  %and = and i64 %a, 18446462598732906495 ; 0xffff00000000ffff
  %or = or i64 %and, 20015998304256       ; 0x0000123456780000
  ret i64 %or
}

;; Pattern 6: a = b | ((c & mask) << shamt)
;; In this testcase b is 0x123456000000789a, but in fact we do not require b
;; being a constant. As long as all positions in b to be overwritten by the
;; incoming bits are known to be zero, the pattern could be matched.
define i64 @pat6(i64 %c) nounwind {
; CHECK-LABEL: pat6:
; CHECK:       # %bb.0:
; CHECK-NEXT:    lu12i.w $a1, 7
; CHECK-NEXT:    ori $a1, $a1, 2202
; CHECK-NEXT:    lu32i.d $a1, 284160
; CHECK-NEXT:    lu52i.d $a1, $a1, 291
; CHECK-NEXT:    bstrins.d $a1, $a0, 39, 16
; CHECK-NEXT:    move $a0, $a1
; CHECK-NEXT:    ret
  %and = and i64 %c, 16777215            ; 0x0000000000ffffff
  %shl = shl i64 %and, 16
  %or = or i64 %shl, 1311767949471676570 ; 0x123456000000789a
  ret i64 %or
}

;; Pattern 7: a = b | ((c << shamt) & shifted_mask)
;; Similar to pattern 6.
define i64 @pat7(i64 %c) nounwind {
; CHECK-LABEL: pat7:
; CHECK:       # %bb.0:
; CHECK-NEXT:    lu12i.w $a1, 7
; CHECK-NEXT:    ori $a1, $a1, 2202
; CHECK-NEXT:    lu32i.d $a1, 284160
; CHECK-NEXT:    lu52i.d $a1, $a1, 291
; CHECK-NEXT:    bstrins.d $a1, $a0, 39, 16
; CHECK-NEXT:    move $a0, $a1
; CHECK-NEXT:    ret
  %shl = shl i64 %c, 16
  %and = and i64 %shl, 1099511562240     ; 0x000000ffffff0000
  %or = or i64 %and, 1311767949471676570 ; 0x123456000000789a
  ret i64 %or
}

;; Pattern 8: a = b | (c & shifted_mask)
;; Similar to pattern 7 but without shift to c.
define i64 @pat8(i64 %c) nounwind {
; CHECK-LABEL: pat8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    srli.d $a1, $a0, 16
; CHECK-NEXT:    lu12i.w $a0, 7
; CHECK-NEXT:    ori $a0, $a0, 2202
; CHECK-NEXT:    lu32i.d $a0, 284160
; CHECK-NEXT:    lu52i.d $a0, $a0, 291
; CHECK-NEXT:    bstrins.d $a0, $a1, 39, 16
; CHECK-NEXT:    ret
  %and = and i64 %c, 1099511562240       ; 0x000000ffffff0000
  %or = or i64 %and, 1311767949471676570 ; 0x123456000000789a
  ret i64 %or
}

;; Test that bstrins.d is not generated because constant OR operand
;; doesn't fit into bits cleared by constant AND operand.
define i64 @no_bstrins_d(i64 %a) nounwind {
; CHECK-LABEL: no_bstrins_d:
; CHECK:       # %bb.0:
; CHECK-NEXT:    lu12i.w $a1, 354185
; CHECK-NEXT:    lu32i.d $a1, 4660
; CHECK-NEXT:    or $a0, $a0, $a1
; CHECK-NEXT:    lu12i.w $a1, 354191
; CHECK-NEXT:    ori $a1, $a1, 4095
; CHECK-NEXT:    lu32i.d $a1, -60876
; CHECK-NEXT:    and $a0, $a0, $a1
; CHECK-NEXT:    ret
  %and = and i64 %a, 18446462598732906495 ; 0xffff00000000ffff
  %or = or i64 %and, 20015998341120       ; 0x0000123456789000
  ret i64 %or
}
